/*-------------------------------------------------------------------------
 *
 * nodeBitmapHeapscan.c
 *	  支持关系的位图扫描的例程
 *
 * 注意：此计划类型仅应与符合 MVCC 的快照（即常规快照，而非 SnapshotAny 或其他特殊快照）一起使用。原因在于，由于索引和堆扫描是解耦的，因此无法保证促使访问特定堆 TID 的索引元组在访问时仍然存在。 因此，元组可能也不再存在（这没关系，因为 heap_fetch 会处理）——但更糟糕的是，该元组槽可能已被重用于较新的元组。在 MVCC 快照中，较新的元组肯定会失败时间条件，因此不会错误地返回，但使用其他任何内容时，我们可能会返回不符合所需索引条件的元组。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/executor/nodeBitmapHeapscan.c
 *
 *-------------------------------------------------------------------------
 */
/*
 * 接口例程
 *		ExecBitmapHeapScan			使用位图信息扫描关系
 *		ExecBitmapHeapNext			上述的工作马
 *		ExecInitBitmapHeapScan		创建并初始化状态信息。
 *		ExecReScanBitmapHeapScan	准备重新扫描计划。
 *		ExecEndBitmapHeapScan		释放所有存储。
 */
#include "postgres.h"

#include <math.h>

#include "access/relscan.h"
#include "access/tableam.h"
#include "access/transam.h"
#include "access/visibilitymap.h"
#include "executor/execdebug.h"
#include "executor/nodeBitmapHeapscan.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "storage/bufmgr.h"
#include "storage/predicate.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/spccache.h"

static TupleTableSlot *fc_BitmapHeapNext(BitmapHeapScanState *fc_node);
static inline void fc_BitmapDoneInitializingSharedState(ParallelBitmapHeapState *fc_pstate);
static inline void fc_BitmapAdjustPrefetchIterator(BitmapHeapScanState *fc_node,
												TBMIterateResult *fc_tbmres);
static inline void fc_BitmapAdjustPrefetchTarget(BitmapHeapScanState *fc_node);
static inline void fc_BitmapPrefetch(BitmapHeapScanState *fc_node,
								  TableScanDesc fc_scan);
static bool fc_BitmapShouldInitializeSharedState(ParallelBitmapHeapState *fc_pstate);


/* ----------------------------------------------------------------
 *		BitmapHeapNext
 *
 *		从 BitmapHeapScan 节点的 currentRelation 中检索下一个元组
 * ----------------------------------------------------------------
 */
static TupleTableSlot * fc_BitmapHeapNext(BitmapHeapScanState *fc_node)
{
	ExprContext *fc_econtext;
	TableScanDesc fc_scan;
	TIDBitmap  *fc_tbm;
	TBMIterator *fc_tbmiterator = NULL;
	TBMSharedIterator *fc_shared_tbmiterator = NULL;
	TBMIterateResult *fc_tbmres;
	TupleTableSlot *fc_slot;
	ParallelBitmapHeapState *fc_pstate = fc_node->pstate;
	dsa_area   *fc_dsa = fc_node->ss.ps.state->es_query_dsa;

	/*
	 * 从索引扫描节点提取必要信息
	 */
	fc_econtext = fc_node->ss.ps.ps_ExprContext;
	fc_slot = fc_node->ss.ss_ScanTupleSlot;
	fc_scan = fc_node->ss.ss_currentScanDesc;
	fc_tbm = fc_node->tbm;
	if (fc_pstate == NULL)
		fc_tbmiterator = fc_node->tbmiterator;
	else
		fc_shared_tbmiterator = fc_node->shared_tbmiterator;
	fc_tbmres = fc_node->tbmres;

	/*
	 * 如果我们尚未执行基础索引扫描，则执行它，并开始遍历位图。
	 *
	 * 对于预取，我们使用 *两个* 迭代器，一个用于我们实际扫描的页面，
	 * 另一个则在第一个前面运行用于预取。node->prefetch_pages 精确跟踪
	 * 预取迭代器领先多少页面。此外，node->prefetch_target 跟踪
	 * 所需的预取距离，该距离从小开始，逐渐增加到 node->prefetch_maximum。
	 * 这样可以避免在因 LIMIT 而在几元组后停止的扫描中进行大量预取。
	 */
	if (!fc_node->initialized)
	{
		if (!fc_pstate)
		{
			fc_tbm = (TIDBitmap *) MultiExecProcNode(outerPlanState(fc_node));

			if (!fc_tbm || !IsA(fc_tbm, TIDBitmap))
				elog(ERROR, "unrecognized result from subplan");

			fc_node->tbm = fc_tbm;
			fc_node->tbmiterator = fc_tbmiterator = tbm_begin_iterate(fc_tbm);
			fc_node->tbmres = fc_tbmres = NULL;

#ifdef USE_PREFETCH
			if (fc_node->prefetch_maximum > 0)
			{
				fc_node->prefetch_iterator = tbm_begin_iterate(fc_tbm);
				fc_node->prefetch_pages = 0;
				fc_node->prefetch_target = -1;
			}
#endif							/* USE_PREFETCH */
		}
		else
		{
			/*
			 * 领导者将立即退出该函数，但其他人将被阻塞，直到领导者填充
			 * TBM 并唤醒他们。
			 */
			if (fc_BitmapShouldInitializeSharedState(fc_pstate))
			{
				fc_tbm = (TIDBitmap *) MultiExecProcNode(outerPlanState(fc_node));
				if (!fc_tbm || !IsA(fc_tbm, TIDBitmap))
					elog(ERROR, "unrecognized result from subplan");

				fc_node->tbm = fc_tbm;

				/*
				 * 准备遍历 TBM。这将返回迭代器状态的 dsa_pointer，
				 * 将被多个进程共同使用进行迭代。
				 */
				fc_pstate->tbmiterator = tbm_prepare_shared_iterate(fc_tbm);
#ifdef USE_PREFETCH
				if (fc_node->prefetch_maximum > 0)
				{
					fc_pstate->prefetch_iterator =
						tbm_prepare_shared_iterate(fc_tbm);

					/*
					 * 在此我们不需要互斥锁，因为我们尚未唤醒其他人。
					 */
					fc_pstate->prefetch_pages = 0;
					fc_pstate->prefetch_target = -1;
				}
#endif

				/* 我们已经初始化了共享状态，因此唤醒其他人。 */
				fc_BitmapDoneInitializingSharedState(fc_pstate);
			}

			/* 分配一个私有迭代器并将共享状态附加到其中 */
			fc_node->shared_tbmiterator = fc_shared_tbmiterator =
				tbm_attach_shared_iterate(fc_dsa, fc_pstate->tbmiterator);
			fc_node->tbmres = fc_tbmres = NULL;

#ifdef USE_PREFETCH
			if (fc_node->prefetch_maximum > 0)
			{
				fc_node->shared_prefetch_iterator =
					tbm_attach_shared_iterate(fc_dsa, fc_pstate->prefetch_iterator);
			}
#endif							/* USE_PREFETCH */
		}
		fc_node->initialized = true;
	}

	for (;;)
	{
		bool		fc_skip_fetch;

		CHECK_FOR_INTERRUPTS();

		/*
		 * 如有需要，获取下一页结果
		 */
		if (fc_tbmres == NULL)
		{
			if (!fc_pstate)
				fc_node->tbmres = fc_tbmres = tbm_iterate(fc_tbmiterator);
			else
				fc_node->tbmres = fc_tbmres = tbm_shared_iterate(fc_shared_tbmiterator);
			if (fc_tbmres == NULL)
			{
				/* 位图中没有更多条目 */
				break;
			}

			fc_BitmapAdjustPrefetchIterator(fc_node, fc_tbmres);

			if (fc_tbmres->ntuples >= 0)
				fc_node->exact_pages++;
			else
				fc_node->lossy_pages++;

			/*
			 * 如果我们不需要堆中的任何字段，并且位图条目不需要重新检查，
			 * 并且页面上的所有元组对我们的事务可见，我们可以跳过获取堆页面。
			 *
			 * XXX：我们在表访存层之上进行这些检查是一种分层违例，它们应该在某个时点移到其下方。
			 */
			fc_skip_fetch = (fc_node->can_skip_fetch &&
						  !fc_tbmres->recheck &&
						  VM_ALL_VISIBLE(fc_node->ss.ss_currentRelation,
										 fc_tbmres->blockno,
										 &fc_node->vmbuffer));

			if (fc_skip_fetch)
			{
				/* 在 skip_fetch 情况下不能有损失 */
				Assert(fc_tbmres->ntuples >= 0);

				/* 
				 * 此页面上的元组数量放入 
				 * node->return_empty_tuples。 
				 */
				fc_node->return_empty_tuples = fc_tbmres->ntuples;
			}
			else if (!table_scan_bitmap_next_block(fc_scan, fc_tbmres))
			{
				/* AM 不认为这个块是有效的，跳过 */
				continue;
			}

			/* 调整预取目标 */
			fc_BitmapAdjustPrefetchTarget(fc_node);
		}
		else
		{
			/* 
			 * 继续在之前获得的页面中。 
			 */

#ifdef USE_PREFETCH

			/* 
			 * 如果在读取第一个元组后我们不停止读取， 
			 * 尝试在我们到达第二个页面之前至少预取几页。 
			 */
			if (!fc_pstate)
			{
				if (fc_node->prefetch_target < fc_node->prefetch_maximum)
					fc_node->prefetch_target++;
			}
			else if (fc_pstate->prefetch_target < fc_node->prefetch_maximum)
			{
				/* 在更新共享状态时采取自旋锁 */
				SpinLockAcquire(&fc_pstate->mutex);
				if (fc_pstate->prefetch_target < fc_node->prefetch_maximum)
					fc_pstate->prefetch_target++;
				SpinLockRelease(&fc_pstate->mutex);
			}
#endif							/* USE_PREFETCH */
		}

		/* 
		 * 我们在获取当前页面*之后*发出预取请求，试图 
		 * 避免预取干扰主 I/O。此外，这 
		 * 应该仅在确认当前页面还有事情要做时发生，否则我们可能会无用地预取 
		 * 我们即将实际请求的相同页面。 
		 * 
		 * XXX：我们在 tableam 之上进行这些检查是层次违规， 
		 * 它们可能应该在某个时刻移到其下面。 
		 */
		fc_BitmapPrefetch(fc_node, fc_scan);

		if (fc_node->return_empty_tuples > 0)
		{
			/* 
			 * 如果我们不需要获取元组，只需返回 null。 
			 */
			ExecStoreAllNullTuple(fc_slot);

			if (--fc_node->return_empty_tuples == 0)
			{
				/* 下一轮没有更多元组可返回 */
				fc_node->tbmres = fc_tbmres = NULL;
			}
		}
		else
		{
			/* 
			 * 尝试从 AM 获取元组。 
			 */
			if (!table_scan_bitmap_next_tuple(fc_scan, fc_tbmres, fc_slot))
			{
				/* 此页面上没有更多内容可查看 */
				fc_node->tbmres = fc_tbmres = NULL;
				continue;
			}

			/* 
			 * 如果我们使用的是有损信息，我们必须在每个元组 
			 * 中重新检查条件。 
			 */
			if (fc_tbmres->recheck)
			{
				fc_econtext->ecxt_scantuple = fc_slot;
				if (!ExecQualAndReset(fc_node->bitmapqualorig, fc_econtext))
				{
					/* 重新检查失败，因此丢弃它并循环回去获取另一个 */
					InstrCountFiltered2(fc_node, 1);
					ExecClearTuple(fc_slot);
					continue;
				}
			}
		}

		/* 可以返回这个元组 */
		return fc_slot;
	}

	/* 
	 * 如果我们到这儿，意味着我们在扫描的末尾…… 
	 */
	return ExecClearTuple(fc_slot);
}

/* 
 *	BitmapDoneInitializingSharedState - 共享状态已初始化 
 * 
 * 到此时，领导者已经填充了 TBM 并初始化了 
 * 共享状态，因此唤醒其他进程。 
 */
static inline void fc_BitmapDoneInitializingSharedState(ParallelBitmapHeapState *fc_pstate)
{
	SpinLockAcquire(&fc_pstate->mutex);
	fc_pstate->state = BM_FINISHED;
	SpinLockRelease(&fc_pstate->mutex);
	ConditionVariableBroadcast(&fc_pstate->cv);
}

/* 
 *	BitmapAdjustPrefetchIterator - 调整预取迭代器 
 */
static inline void fc_BitmapAdjustPrefetchIterator(BitmapHeapScanState *fc_node,
							 TBMIterateResult *fc_tbmres)
{
#ifdef USE_PREFETCH
	ParallelBitmapHeapState *fc_pstate = fc_node->pstate;

	if (fc_pstate == NULL)
	{
		TBMIterator *fc_prefetch_iterator = fc_node->prefetch_iterator;

		if (fc_node->prefetch_pages > 0)
		{
			/* 主迭代器已关闭距离一页 */
			fc_node->prefetch_pages--;
		}
		else if (fc_prefetch_iterator)
		{
			/* 不要让预取迭代器落后于主迭代器 */
			TBMIterateResult *fc_tbmpre = tbm_iterate(fc_prefetch_iterator);

			if (fc_tbmpre == NULL || fc_tbmpre->blockno != fc_tbmres->blockno)
				elog(ERROR, "prefetch and main iterators are out of sync");
		}
		return;
	}

	if (fc_node->prefetch_maximum > 0)
	{
		TBMSharedIterator *fc_prefetch_iterator = fc_node->shared_prefetch_iterator;

		SpinLockAcquire(&fc_pstate->mutex);
		if (fc_pstate->prefetch_pages > 0)
		{
			fc_pstate->prefetch_pages--;
			SpinLockRelease(&fc_pstate->mutex);
		}
		else
		{
			/* 在迭代之前释放互斥锁 */
			SpinLockRelease(&fc_pstate->mutex);

			/* 
			 * 在共享模式下，我们无法确保当前 
			 * 主迭代器的块号和预取迭代器的块号 
			 * 相同。可能预取的块号将由其他进程处理。 
			 * 因此，我们在这里不验证块号，与非并行 
			 * 情况不同。 
			 */
			if (fc_prefetch_iterator)
				tbm_shared_iterate(fc_prefetch_iterator);
		}
	}
#endif							/* USE_PREFETCH */
}

/* 
 * BitmapAdjustPrefetchTarget - 调整预取目标 
 * 
 * 如果预取目标尚未达到最大，则增加预取目标。注意， 
 * 我们将在获取第一个页面/元组后将其增加到零，然后在获取第二个元组后 
 * 增加到一，然后在获取后续页面时翻倍。 
 */
static inline void fc_BitmapAdjustPrefetchTarget(BitmapHeapScanState *fc_node)
{
#ifdef USE_PREFETCH
	ParallelBitmapHeapState *fc_pstate = fc_node->pstate;

	if (fc_pstate == NULL)
	{
		if (fc_node->prefetch_target >= fc_node->prefetch_maximum)
			 /* 不再增加 */ ;
		else if (fc_node->prefetch_target >= fc_node->prefetch_maximum / 2)
			fc_node->prefetch_target = fc_node->prefetch_maximum;
		else if (fc_node->prefetch_target > 0)
			fc_node->prefetch_target *= 2;
		else
			fc_node->prefetch_target++;
		return;
	}

	/* 首先进行无锁检查以节省自旋锁获取。 */
	if (fc_pstate->prefetch_target < fc_node->prefetch_maximum)
	{
		SpinLockAcquire(&fc_pstate->mutex);
		if (fc_pstate->prefetch_target >= fc_node->prefetch_maximum)
			 /* 不再增加 */ ;
		else if (fc_pstate->prefetch_target >= fc_node->prefetch_maximum / 2)
			fc_pstate->prefetch_target = fc_node->prefetch_maximum;
		else if (fc_pstate->prefetch_target > 0)
			fc_pstate->prefetch_target *= 2;
		else
			fc_pstate->prefetch_target++;
		SpinLockRelease(&fc_pstate->mutex);
	}
#endif							/* USE_PREFETCH */
}

/* 
 * BitmapPrefetch - 预取，如果 prefetch_pages 落后于 prefetch_target 
 */
static inline void fc_BitmapPrefetch(BitmapHeapScanState *fc_node, TableScanDesc fc_scan)
{
#ifdef USE_PREFETCH
	ParallelBitmapHeapState *fc_pstate = fc_node->pstate;

	if (fc_pstate == NULL)
	{
		TBMIterator *fc_prefetch_iterator = fc_node->prefetch_iterator;

		if (fc_prefetch_iterator)
		{
			while (fc_node->prefetch_pages < fc_node->prefetch_target)
			{
				TBMIterateResult *fc_tbmpre = tbm_iterate(fc_prefetch_iterator);
				bool		fc_skip_fetch;

				if (fc_tbmpre == NULL)
				{
					/* 没有更多页面可以预取 */
					tbm_end_iterate(fc_prefetch_iterator);
					fc_node->prefetch_iterator = NULL;
					break;
				}
				fc_node->prefetch_pages++;

				/* 
				 * 如果我们预计不必实际读取此堆页面， 
				 * 跳过此预取调用，但继续正常运行预取 
				 * 逻辑。（增加 prefetch_pages 会更好吗？） 
				 * 
				 * 这取决于索引 AM 将为此未来堆页面 
				 * 报告与当前堆页面相同的重新检查标志的假设； 
				 * 这不是确定的，但在许多情况下都是正确的。 
				 */
				fc_skip_fetch = (fc_node->can_skip_fetch &&
							  (fc_node->tbmres ? !fc_node->tbmres->recheck : false) &&
							  VM_ALL_VISIBLE(fc_node->ss.ss_currentRelation,
											 fc_tbmpre->blockno,
											 &fc_node->pvmbuffer));

				if (!fc_skip_fetch)
					PrefetchBuffer(fc_scan->rs_rd, MAIN_FORKNUM, fc_tbmpre->blockno);
			}
		}

		return;
	}

	if (fc_pstate->prefetch_pages < fc_pstate->prefetch_target)
	{
		TBMSharedIterator *fc_prefetch_iterator = fc_node->shared_prefetch_iterator;

		if (fc_prefetch_iterator)
		{
			while (1)
			{
				TBMIterateResult *fc_tbmpre;
				bool		fc_do_prefetch = false;
				bool		fc_skip_fetch;

				/* 
				 * 在互斥锁下重新检查。如果其他进程已经 
				 * 进行了足够的预取，则我们不需要执行任何操作。 
				 */
				SpinLockAcquire(&fc_pstate->mutex);
				if (fc_pstate->prefetch_pages < fc_pstate->prefetch_target)
				{
					fc_pstate->prefetch_pages++;
					fc_do_prefetch = true;
				}
				SpinLockRelease(&fc_pstate->mutex);

				if (!fc_do_prefetch)
					return;

				fc_tbmpre = tbm_shared_iterate(fc_prefetch_iterator);
				if (fc_tbmpre == NULL)
				{
					/* 没有更多页面可以预取 */
					tbm_end_shared_iterate(fc_prefetch_iterator);
					fc_node->shared_prefetch_iterator = NULL;
					break;
				}

				/* 如上所述，如果我们预计不需要页面，则跳过预取 */
				fc_skip_fetch = (fc_node->can_skip_fetch &&
							  (fc_node->tbmres ? !fc_node->tbmres->recheck : false) &&
							  VM_ALL_VISIBLE(fc_node->ss.ss_currentRelation,
											 fc_tbmpre->blockno,
											 &fc_node->pvmbuffer));

				if (!fc_skip_fetch)
					PrefetchBuffer(fc_scan->rs_rd, MAIN_FORKNUM, fc_tbmpre->blockno);
			}
		}
	}
#endif							/* USE_PREFETCH */
}

/*
 * BitmapHeapRecheck -- 访问方法例程以在EvalPlanQual中重新检查元组
 */
static bool fc_BitmapHeapRecheck(BitmapHeapScanState *fc_node, TupleTableSlot *fc_slot)
{
	ExprContext *fc_econtext;

	/*
	 * 从索引扫描节点提取必要信息
	 */
	fc_econtext = fc_node->ss.ps.ps_ExprContext;

	/* 该元组是否符合原始条件？ */
	fc_econtext->ecxt_scantuple = fc_slot;
	return ExecQualAndReset(fc_node->bitmapqualorig, fc_econtext);
}

/* ----------------------------------------------------------------
 *		ExecBitmapHeapScan(node)
 * ----------------------------------------------------------------
 */
static TupleTableSlot * fc_ExecBitmapHeapScan(PlanState *fc_pstate)
{
	BitmapHeapScanState *fc_node = castNode(BitmapHeapScanState, fc_pstate);

	return ExecScan(&fc_node->ss,
					(ExecScanAccessMtd) fc_BitmapHeapNext,
					(ExecScanRecheckMtd) fc_BitmapHeapRecheck);
}

/* ----------------------------------------------------------------
 *		ExecReScanBitmapHeapScan(node)
 * ----------------------------------------------------------------
 */
void ExecReScanBitmapHeapScan(BitmapHeapScanState *fc_node)
{
	PlanState  *fc_outerPlan = outerPlanState(fc_node);

	/* 重新扫描以释放任何页面固定 */
	table_rescan(fc_node->ss.ss_currentScanDesc, NULL);

	/* 如果有，释放位图和缓冲区 */
	if (fc_node->tbmiterator)
		tbm_end_iterate(fc_node->tbmiterator);
	if (fc_node->prefetch_iterator)
		tbm_end_iterate(fc_node->prefetch_iterator);
	if (fc_node->shared_tbmiterator)
		tbm_end_shared_iterate(fc_node->shared_tbmiterator);
	if (fc_node->shared_prefetch_iterator)
		tbm_end_shared_iterate(fc_node->shared_prefetch_iterator);
	if (fc_node->tbm)
		tbm_free(fc_node->tbm);
	if (fc_node->vmbuffer != InvalidBuffer)
		ReleaseBuffer(fc_node->vmbuffer);
	if (fc_node->pvmbuffer != InvalidBuffer)
		ReleaseBuffer(fc_node->pvmbuffer);
	fc_node->tbm = NULL;
	fc_node->tbmiterator = NULL;
	fc_node->tbmres = NULL;
	fc_node->prefetch_iterator = NULL;
	fc_node->initialized = false;
	fc_node->shared_tbmiterator = NULL;
	fc_node->shared_prefetch_iterator = NULL;
	fc_node->vmbuffer = InvalidBuffer;
	fc_node->pvmbuffer = InvalidBuffer;

	ExecScanReScan(&fc_node->ss);

	/*
	 * 如果子节点的chgParam不为空，则计划将由第一个ExecProcNode重新扫描。
	 */
	if (fc_outerPlan->chgParam == NULL)
		ExecReScan(fc_outerPlan);
}

/* ----------------------------------------------------------------
 *		ExecEndBitmapHeapScan
 * ----------------------------------------------------------------
 */
void ExecEndBitmapHeapScan(BitmapHeapScanState *fc_node)
{
	TableScanDesc fc_scanDesc;

	/*
	 * 从节点中提取信息
	 */
	fc_scanDesc = fc_node->ss.ss_currentScanDesc;

	/*
	 * 释放表达式上下文
	 */
	ExecFreeExprContext(&fc_node->ss.ps);

	/*
	 * 清空元组表插槽
	 */
	if (fc_node->ss.ps.ps_ResultTupleSlot)
		ExecClearTuple(fc_node->ss.ps.ps_ResultTupleSlot);
	ExecClearTuple(fc_node->ss.ss_ScanTupleSlot);

	/*
	 * 关闭子计划
	 */
	ExecEndNode(outerPlanState(fc_node));

	/*
	 * 如果有，释放位图和缓冲区
	 */
	if (fc_node->tbmiterator)
		tbm_end_iterate(fc_node->tbmiterator);
	if (fc_node->prefetch_iterator)
		tbm_end_iterate(fc_node->prefetch_iterator);
	if (fc_node->tbm)
		tbm_free(fc_node->tbm);
	if (fc_node->shared_tbmiterator)
		tbm_end_shared_iterate(fc_node->shared_tbmiterator);
	if (fc_node->shared_prefetch_iterator)
		tbm_end_shared_iterate(fc_node->shared_prefetch_iterator);
	if (fc_node->vmbuffer != InvalidBuffer)
		ReleaseBuffer(fc_node->vmbuffer);
	if (fc_node->pvmbuffer != InvalidBuffer)
		ReleaseBuffer(fc_node->pvmbuffer);

	/*
	 * 关闭堆扫描
	 */
	table_endscan(fc_scanDesc);
}

/* ----------------------------------------------------------------
 *		ExecInitBitmapHeapScan
 *
 *		初始化扫描的状态信息。
 * ----------------------------------------------------------------
 */
BitmapHeapScanState *
ExecInitBitmapHeapScan(BitmapHeapScan *fc_node, EState *fc_estate, int fc_eflags)
{
	BitmapHeapScanState *fc_scanstate;
	Relation	fc_currentRelation;

	/* 检查不支持的标志 */
	Assert(!(fc_eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));

	/*
	 * 断言调用者没有请求不安全的快照 --- 见文件开头的注释。
	 */
	Assert(IsMVCCSnapshot(fc_estate->es_snapshot));

	/*
	 * 创建状态结构
	 */
	fc_scanstate = makeNode(BitmapHeapScanState);
	fc_scanstate->ss.ps.plan = (Plan *) fc_node;
	fc_scanstate->ss.ps.state = fc_estate;
	fc_scanstate->ss.ps.ExecProcNode = fc_ExecBitmapHeapScan;

	fc_scanstate->tbm = NULL;
	fc_scanstate->tbmiterator = NULL;
	fc_scanstate->tbmres = NULL;
	fc_scanstate->return_empty_tuples = 0;
	fc_scanstate->vmbuffer = InvalidBuffer;
	fc_scanstate->pvmbuffer = InvalidBuffer;
	fc_scanstate->exact_pages = 0;
	fc_scanstate->lossy_pages = 0;
	fc_scanstate->prefetch_iterator = NULL;
	fc_scanstate->prefetch_pages = 0;
	fc_scanstate->prefetch_target = 0;
	fc_scanstate->pscan_len = 0;
	fc_scanstate->initialized = false;
	fc_scanstate->shared_tbmiterator = NULL;
	fc_scanstate->shared_prefetch_iterator = NULL;
	fc_scanstate->pstate = NULL;

	/*
	 * 如果不需要任何表的列，用于检查不可索引条件或返回数据，我们可以跳过获取堆页面。
	 * 这个测试有点简单，因为它检查的是更强的条件，即根本没有条件或返回 tlist。
	 * 但在大多数情况下，可能不值得比这更努力。
	 */
	fc_scanstate->can_skip_fetch = (fc_node->scan.plan.qual == NIL &&
								 fc_node->scan.plan.targetlist == NIL);

	/*
	 * 杂项初始化
	 *
	 * 为节点创建表达式上下文
	 */
	ExecAssignExprContext(fc_estate, &fc_scanstate->ss.ps);

	/*
	 * 打开扫描关系
	 */
	fc_currentRelation = ExecOpenScanRelation(fc_estate, fc_node->scan.scanrelid, fc_eflags);

	/*
	 * 初始化子节点
	 */
	outerPlanState(fc_scanstate) = ExecInitNode(outerPlan(fc_node), fc_estate, fc_eflags);

	/*
	 * 从关系描述符获取扫描类型。
	 */
	ExecInitScanTupleSlot(fc_estate, &fc_scanstate->ss,
						  RelationGetDescr(fc_currentRelation),
						  table_slot_callbacks(fc_currentRelation));

	/*
	 * 初始化结果类型和投影。
	 */
	ExecInitResultTypeTL(&fc_scanstate->ss.ps);
	ExecAssignScanProjectionInfo(&fc_scanstate->ss);

	/*
	 * 初始化子表达式
	 */
	fc_scanstate->ss.ps.qual =
		ExecInitQual(fc_node->scan.plan.qual, (PlanState *) fc_scanstate);
	fc_scanstate->bitmapqualorig =
		ExecInitQual(fc_node->bitmapqualorig, (PlanState *) fc_scanstate);

	/*
	 * 如果配置了表空间，则是最大预读取数量，
	 * 否则为有效的io_concurrency GUC的当前值。
	 */
	fc_scanstate->prefetch_maximum =
		get_tablespace_io_concurrency(fc_currentRelation->rd_rel->reltablespace);

	fc_scanstate->ss.ss_currentRelation = fc_currentRelation;

	fc_scanstate->ss.ss_currentScanDesc = table_beginscan_bm(fc_currentRelation,
														  fc_estate->es_snapshot,
														  0,
														  NULL);

	/*
	 * 完成所有操作。
	 */
	return fc_scanstate;
}

/*----------------
 *		BitmapShouldInitializeSharedState
 *
 *		第一个来到这里并看到状态为BM_INITIAL的进程将成为
 *		并行位图扫描的领导者，并负责填充TIDBitmap。
 *		其他进程将被条件变量阻塞，直到领导者唤醒他们。
 * ---------------
 */
static bool fc_BitmapShouldInitializeSharedState(ParallelBitmapHeapState *fc_pstate)
{
	SharedBitmapState fc_state;

	while (1)
	{
		SpinLockAcquire(&fc_pstate->mutex);
		fc_state = fc_pstate->state;
		if (fc_pstate->state == BM_INITIAL)
			fc_pstate->state = BM_INPROGRESS;
		SpinLockRelease(&fc_pstate->mutex);

		/* 如果位图已经完成，或者我们是领导者则退出。 */
		if (fc_state != BM_INPROGRESS)
			break;

		/* 等待领导者唤醒我们。 */
		ConditionVariableSleep(&fc_pstate->cv, WAIT_EVENT_PARALLEL_BITMAP_SCAN);
	}

	ConditionVariableCancelSleep();

	return (fc_state == BM_INITIAL);
}

/* ----------------------------------------------------------------
 *		ExecBitmapHeapEstimate
 *
 *		计算我们在并行查询DSM中需要的空间量，并告知pcxt->estimator我们的需求。
 * ----------------------------------------------------------------
 */
void ExecBitmapHeapEstimate(BitmapHeapScanState *fc_node,
					   ParallelContext *fc_pcxt)
{
	EState	   *fc_estate = fc_node->ss.ps.state;

	fc_node->pscan_len = add_size(offsetof(ParallelBitmapHeapState,
										phs_snapshot_data),
							   EstimateSnapshotSpace(fc_estate->es_snapshot));

	shm_toc_estimate_chunk(&fc_pcxt->estimator, fc_node->pscan_len);
	shm_toc_estimate_keys(&fc_pcxt->estimator, 1);
}

/* ----------------------------------------------------------------
 *		ExecBitmapHeapInitializeDSM
 *
 *		设置并行位图堆扫描描述符。
 * ----------------------------------------------------------------
 */
void ExecBitmapHeapInitializeDSM(BitmapHeapScanState *fc_node,
							ParallelContext *fc_pcxt)
{
	ParallelBitmapHeapState *fc_pstate;
	EState	   *fc_estate = fc_node->ss.ps.state;
	dsa_area   *fc_dsa = fc_node->ss.ps.state->es_query_dsa;

	/* 如果没有DSA，就没有工作线程；不进行初始化。 */
	if (fc_dsa == NULL)
		return;

	fc_pstate = shm_toc_allocate(fc_pcxt->toc, fc_node->pscan_len);

	fc_pstate->tbmiterator = 0;
	fc_pstate->prefetch_iterator = 0;

	/* 初始化互斥量 */
	SpinLockInit(&fc_pstate->mutex);
	fc_pstate->prefetch_pages = 0;
	fc_pstate->prefetch_target = 0;
	fc_pstate->state = BM_INITIAL;

	ConditionVariableInit(&fc_pstate->cv);
	SerializeSnapshot(fc_estate->es_snapshot, fc_pstate->phs_snapshot_data);

	shm_toc_insert(fc_pcxt->toc, fc_node->ss.ps.plan->plan_node_id, fc_pstate);
	fc_node->pstate = fc_pstate;
}

/* ----------------------------------------------------------------
 *		ExecBitmapHeapReInitializeDSM
 *
 *		在开始新的扫描之前重置共享状态。
 * ----------------------------------------------------------------
 */
void ExecBitmapHeapReInitializeDSM(BitmapHeapScanState *fc_node,
							  ParallelContext *fc_pcxt)
{
	ParallelBitmapHeapState *fc_pstate = fc_node->pstate;
	dsa_area   *fc_dsa = fc_node->ss.ps.state->es_query_dsa;

	/* 如果没有DSA，就没有工作线程；不做任何事情。 */
	if (fc_dsa == NULL)
		return;

	fc_pstate->state = BM_INITIAL;

	if (DsaPointerIsValid(fc_pstate->tbmiterator))
		tbm_free_shared_area(fc_dsa, fc_pstate->tbmiterator);

	if (DsaPointerIsValid(fc_pstate->prefetch_iterator))
		tbm_free_shared_area(fc_dsa, fc_pstate->prefetch_iterator);

	fc_pstate->tbmiterator = InvalidDsaPointer;
	fc_pstate->prefetch_iterator = InvalidDsaPointer;
}

/* ----------------------------------------------------------------
 *		ExecBitmapHeapInitializeWorker
 *
 *		从TOC复制相关信息到计划状态。
 * ----------------------------------------------------------------
 */
void ExecBitmapHeapInitializeWorker(BitmapHeapScanState *fc_node,
							   ParallelWorkerContext *fc_pwcxt)
{
	ParallelBitmapHeapState *fc_pstate;
	Snapshot	fc_snapshot;

	Assert(fc_node->ss.ps.state->es_query_dsa != NULL);

	fc_pstate = shm_toc_lookup(fc_pwcxt->toc, fc_node->ss.ps.plan->plan_node_id, false);
	fc_node->pstate = fc_pstate;

	fc_snapshot = RestoreSnapshot(fc_pstate->phs_snapshot_data);
	table_scan_update_snapshot(fc_node->ss.ss_currentScanDesc, fc_snapshot);
}
